package com.app.tools;

import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.google.gson.Gson;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 图片处理工具类
 */
public class ToolImageUtil {

    /**
     * 获取单张图片的全量Info信息
     *
     * @param path 图片路径
     */
    @SuppressWarnings("rawtypes")
    public static Map<String, String> getImageInfo(String path) {
        Map<String, String> imageInfo = new HashMap<String, String>();
        try {
            File jpegFile = new File(path);
            if (jpegFile.isFile()) {// 图片必须是一个文件
                Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);
                Iterator tags = null;
                Tag tag = null;
                for (Directory exif : metadata.getDirectories()) {
                    tags = exif.getTags().iterator();
                    while (tags.hasNext()) {
                        tag = (Tag) tags.next();
                        imageInfo.put(tag.getTagName().trim().replace(" ", ""), tag.getDescription().trim().replace(" ", ""));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return imageInfo;
    }

    /**
     * 获取单张图片指定Info类型的信息
     *
     * @param path			图片路径
     * @param direct		Info类型[eg:InfoSubIFDDirectory or PhotoshopDirectory]
     */
    @SuppressWarnings("rawtypes")
    public static Map<String, String> getImageInfo(String path, Class<? extends Directory> direct) {
        Map<String, String> imageInfo = new HashMap<String, String>();
        try {
            File jpegFile = new File(path);
            if (jpegFile.isFile()) {
                Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);
                Directory exif = metadata.getFirstDirectoryOfType(direct);
                Iterator tags = null;
                Tag tag = null;
                if (exif != null) {
                    tags = exif.getTags().iterator();
                    while (tags.hasNext()) {
                        tag = (Tag) tags.next();
                        imageInfo.put(tag.getTagName().trim().replace(" ", ""), tag.getDescription().trim().replace(" ", ""));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return imageInfo;
    }

    /**
     * 校验图片分辨率
     *
     * @param path		图片路径
     * @param height	图片的高
     * @param width		图片的宽
     */
    public static boolean checkImageResolution(String path, String height, String width) {
        Map<String, String> imageInfo = getImageInfo(path);
        String currentImageHeight = imageInfo.get("ImageHeight").replace("pixels", "");
        String currentImageWidth = imageInfo.get("ImageWidth").replace("pixels", "");
        if (height.equals(currentImageHeight) && width.equals(currentImageWidth)) {
            return true;
        }
        return false;
    }

    /**
     * 转二值化图片
     *
     * @param path		图片路径
     * @throws IOException
     */
    public static void toBinaryImage(String path) throws IOException {
        String uri = path.substring(0, path.lastIndexOf("."));
        String suffix = path.substring(path.lastIndexOf("."), path.length());

        File file = new File(path);
        BufferedImage image = ImageIO.read(file);

        int width = image.getWidth();
        int height = image.getHeight();

        BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);// 重点，技巧在这个参数BufferedImage.TYPE_BYTE_BINARY
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int rgb = image.getRGB(i, j);
                grayImage.setRGB(i, j, rgb);
            }
        }

        File newFile = new File(uri + "_binary" + suffix);
        ImageIO.write(grayImage, "jpg", newFile);
    }

    /**
     * 转灰度图
     *
     * @param path		图片路径
     * @throws IOException
     */
    public static void toGrayImage(String path) throws IOException {
        String uri = path.substring(0, path.lastIndexOf("."));
        String suffix = path.substring(path.lastIndexOf("."), path.length());

        File file = new File(path);
        BufferedImage image = ImageIO.read(file);

        int width = image.getWidth();
        int height = image.getHeight();

        BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);// 重点，技巧在这个参数BufferedImage.TYPE_BYTE_GRAY
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int rgb = image.getRGB(i, j);
                grayImage.setRGB(i, j, rgb);
            }
        }

        File newFile = new File(uri + "_gray" + suffix);
        ImageIO.write(grayImage, suffix.substring(1, suffix.length()), newFile);
    }

    /**
     * 缩放图像
     *
     * @param path				图片路径
     * @param result			缩放后的图像地址
     * @param scale				缩放比例
     * @param flag				缩放选择:true 放大; false 缩小;
     */
    public static void scale(String path, String result, int scale, boolean flag) {
        try {
            String uri = path.substring(0, path.lastIndexOf("."));
            String suffix = path.substring(path.lastIndexOf(".") + 1, path.length());
            BufferedImage src = ImageIO.read(new File(path)); // 读入文件
            int width = src.getWidth(); 	// 得到源图宽
            int height = src.getHeight(); 	// 得到源图长
            if (flag) {
                width = width * scale;
                height = height * scale;
            } else {
                width = width / scale;
                height = height / scale;
            }
            // 原图片的缩放比例对象
            Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
            // 空白的缓冲字符图象
            BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            // 绘制对象
            Graphics g = tag.getGraphics();
            g.drawImage(image, 0, 0, null); // 开始绘制缩小后的图
            g.dispose();					// 最终形成了一个图像

            if(null==result || "".equals(result)){
                result = uri + "_" + flag + scale + "." + suffix;
            }

            ImageIO.write(tag, suffix, new File(result));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 切割图片
     *
     * @param path		图片路径
     * @param result	新生成的图片位置
     * @param x			截点横坐标 (从左开始计数)
     * @param y			截点纵坐标 (从上开始计数)
     * @param width		截取的宽度
     * @param height	截取的长度
     */
    public static void cutImage(String path, String result, int x, int y, int width, int height) {

        FileInputStream is = null;
        ImageInputStream iis = null;

        String uri = path.substring(0, path.lastIndexOf("."));
        String suffix = path.substring(path.lastIndexOf(".") + 1, path.length());

        try {
            is = new FileInputStream(path);
            Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(suffix);
            ImageReader reader = it.next();
            iis = ImageIO.createImageInputStream(is);
            reader.setInput(iis, true);
            ImageReadParam param = reader.getDefaultReadParam();
            Point p = new Point();
            p.setLocation(x, y);

            Dimension d = new Dimension();
            d.setSize(width, height);
            Rectangle rect = new Rectangle(p, d);
            param.setSourceRegion(rect);

            BufferedImage bi = reader.read(0, param);
            if(null==result || "".equals(result)){
                result = uri + "_cut" + "." + suffix;
            }
            ImageIO.write(bi, suffix, new File(result));

            is.close();
            iis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * 图像类型转换 GIF->JPG GIF->PNG PNG->JPG PNG->GIF(X)
     * @param path		图片路径
     * @param result	新生成的图片路径
     */
    public static void convert(String path, String result, String suffix) {
        try {
            String uri = path.substring(0, path.lastIndexOf("."));
            File f = new File(path);
            f.canRead();
            f.canWrite();
            BufferedImage src = ImageIO.read(f);
            if(null==result || "".equals(result)){
                result = uri + "_to" + "." + suffix;
            }
            ImageIO.write(src, path.substring(path.lastIndexOf(".") + 1, path.length()), new File(result));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取图片RGB数据
     * @param path
     * @return
     * @throws IOException
     */
    public static String[][] getImageRGB(String path) throws IOException{
        String[][] arr = null;
        int[] rgb = new int[3];
        File file = new File(path);
        BufferedImage bi = ImageIO.read(file);
        int width = bi.getWidth();
        int height = bi.getHeight();
        int minx = bi.getMinX();
        int miny = bi.getMinY();
        arr = new String[width][height];
        for (int i = minx; i < width; i++) {
            for (int j = miny; j < height; j++) {
                int pixel = bi.getRGB(i, j); // 下面三行代码将一个数字转换为RGB数字
                rgb[0] = (pixel & 0xff0000) >> 16;
                rgb[1] = (pixel & 0xff00) >> 8;
                rgb[2] = (pixel & 0xff);

                arr[i][j] =  rgb[0] + "," + rgb[1] + "," + rgb[2];
            }
        }
        return arr;
    }

    /**
     * 生成两张图片差图
     *
     * @param firstPath		    第一图片地址
     * @param secondPath		第二图片地址
     * @param result		    存放地址
     * @throws IOException
     */
    public static void imageDifference(String firstPath, String secondPath, String result) throws IOException{
        String uri = firstPath.substring(0, firstPath.lastIndexOf("."));
        String suffix = firstPath.substring(firstPath.lastIndexOf(".") + 1, firstPath.length());

        BufferedImage bi = ImageIO.read(new File(firstPath));
        String[][] firstArr = getImageRGB(firstPath);
        String[][] secondArr = getImageRGB(secondPath);
        int width = firstArr.length;
        int height = firstArr[0].length;

        Color targetColor = null;
        Map<String, Integer> rgbMap = null;
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                rgbMap = getRGBCount(secondArr[i][j], firstArr[i][j]);
                targetColor = new Color(rgbMap.get("R"), rgbMap.get("G"), rgbMap.get("B"));
                bi.setRGB(i, j, targetColor.getRGB());
            }
        }

        if(null==result || "".equals(result)){
            result = uri + "_cha" + "." + suffix;
        }

        Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName(suffix);
        ImageWriter writer = it.next();

        File f = new File(result);
        ImageOutputStream ios = ImageIO.createImageOutputStream(f);
        writer.setOutput(ios);
        writer.write(bi);
        bi.flush();
        ios.flush();
        ios.close();

        }

        /**
         * RGB 计算
         * @param rgb1
         * @param rgb2
         * @return
         */
        public static Map<String, Integer> getRGBCount(String rgb1, String rgb2) {

            Map<String, Integer> rgb = new HashMap<>();
            String[] arr1 = rgb1.split(",");
            String[] arr2 = rgb2.split(",");
            int r = 0, g = 0, b = 0;
            if(!arr1[0].equals(arr2[0])){
            r = Integer.parseInt(arr2[0]);
        }

        if(!arr1[1].equals(arr2[1])){
            r = Integer.parseInt(arr2[1]);
        }

        if(!arr1[2].equals(arr2[2])){
            r = Integer.parseInt(arr2[2]);
        }

        rgb.put("R", r);
        rgb.put("G", g);
        rgb.put("B", b);

        return rgb;
    }


    /**
     * base64 转 base64灰度图
     * @param base64Content
     * @return
     */
    @SuppressWarnings("restriction")
    public static String byte64ToBufferedImage(String base64Content){
        String resContent = "";
        if (base64Content == null) // 图像数据为空
            return "";
        try {
            // Base64解码
            byte[] bytes = new sun.misc.BASE64Decoder().decodeBuffer(base64Content);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 调整异常数据
                    bytes[i] += 256;
                }
            }
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);    //将b作为输入流；
            BufferedImage image = ImageIO.read(in);

            int width = image.getWidth();
            int height = image.getHeight();

            BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);// 重点，技巧在这个参数BufferedImage.TYPE_BYTE_GRAY
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    int rgb = image.getRGB(i, j);
                    grayImage.setRGB(i, j, rgb);
                }
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ImageIO.write(grayImage, "png", out);
            resContent = new sun.misc.BASE64Encoder().encode(out.toByteArray());
            System.out.println(resContent);
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        return resContent;
    }

    /**
     * 生成降低质量图片
     * @param originalFile
     * @param resizedFile
     * @param newWidth
     * @param quality
     * @throws IOException
     */
    @SuppressWarnings("restriction")
    public static void resize(File originalFile, File resizedFile, int newWidth, float quality) throws IOException {

        if (quality > 1) {
            throw new IllegalArgumentException("Quality has to be between 0 and 1");
        }

        ImageIcon ii = new ImageIcon(originalFile.getCanonicalPath());
        Image i = ii.getImage();
        Image resizedImage = null;

        int iWidth = i.getWidth(null);
        int iHeight = i.getHeight(null);

        if (iWidth < newWidth) {
            newWidth = iWidth;
        }
        if (iWidth > iHeight) {
            resizedImage = i.getScaledInstance(newWidth, (newWidth * iHeight) / iWidth, Image.SCALE_SMOOTH);
        } else {
            resizedImage = i.getScaledInstance((newWidth * iWidth) / iHeight, newWidth, Image.SCALE_SMOOTH);
        }

        // This code ensures that all the pixels in the image are loaded.
        Image temp = new ImageIcon(resizedImage).getImage();

        // Create the buffered image.
        BufferedImage bufferedImage = new BufferedImage(temp.getWidth(null), temp.getHeight(null),
                BufferedImage.TYPE_INT_RGB);

        // Copy image to buffered image.
        Graphics g = bufferedImage.createGraphics();

        // Clear background and paint the image.
        g.setColor(Color.white);
        g.fillRect(0, 0, temp.getWidth(null), temp.getHeight(null));
        g.drawImage(temp, 0, 0, null);
        g.dispose();

        // Soften.
        float softenFactor = 0.05f;
        float[] softenArray = { 0, softenFactor, 0, softenFactor, 1 - (softenFactor * 4), softenFactor, 0, softenFactor, 0 };
        Kernel kernel = new Kernel(3, 3, softenArray);
        ConvolveOp cOp = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
        bufferedImage = cOp.filter(bufferedImage, null);

        // Write the jpeg to a file.
        FileOutputStream out = new FileOutputStream(resizedFile);

        // Encodes image as a JPEG data stream
        com.sun.image.codec.jpeg.JPEGImageEncoder encoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(out);
        com.sun.image.codec.jpeg.JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bufferedImage);

        param.setQuality(quality, true);

        encoder.setJPEGEncodeParam(param);
        encoder.encode(bufferedImage);
    }


    /**
     * 获取图片base64数据
     * @param imgFilePath
     * @return
     */
    @SuppressWarnings("restriction")
    public static String getImageStr(String imgFilePath) {
        byte[] data = null;
        // 读取图片字节数组
        try {
            InputStream in = new FileInputStream(imgFilePath);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 对字节数组Base64编码
        return new sun.misc.BASE64Encoder().encode(data);
    }

    /**
     * 对字节数组字符串进行Base64解码并生成图片
     *
     * @param base64Content
     * @param imgFilePath
     * @return
     */
    @SuppressWarnings("restriction")
    public static boolean generateImage(String base64Content, String imgFilePath) {
        if (base64Content == null) // 图像数据为空
            return false;
        try {
            // Base64解码
            byte[] bytes = new sun.misc.BASE64Decoder().decodeBuffer(base64Content);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 调整异常数据
                    bytes[i] += 256;
                }
            }
            // 生成jpeg图片
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(bytes);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * base64转缩小base64灰度图
     * @param base64Content
     * @param quality (0.01 -1)
     *
     * @return base64 字符串
     */
    @SuppressWarnings("restriction")
    public static String byte64ToZoomByte64Gray(String base64Content, float quality) {
        String resContent = "";
        if (base64Content == null) // 图像数据为空
            return "";
        try {
            // Base64解码
            byte[] bytes = new sun.misc.BASE64Decoder().decodeBuffer(base64Content);
            for (int i = 0; i < bytes.length; ++i) {
                if (bytes[i] < 0) {// 调整异常数据
                    bytes[i] += 256;
                }
            }
            ByteArrayInputStream in = new ByteArrayInputStream(bytes); // 将b作为输入流；
            BufferedImage image = ImageIO.read(in);

            int width = image.getWidth();
            int height = image.getHeight();

            // 重点，技巧在这个参数BufferedImage.TYPE_BYTE_GRAY
            BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    int rgb = image.getRGB(i, j);
                    grayImage.setRGB(i, j, rgb);
                }
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();

            com.sun.image.codec.jpeg.JPEGImageEncoder encoder = com.sun.image.codec.jpeg.JPEGCodec
                    .createJPEGEncoder(out);
            com.sun.image.codec.jpeg.JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(grayImage);

            param.setQuality(quality, true);

            encoder.setJPEGEncodeParam(param);
            encoder.encode(grayImage);

            resContent = new sun.misc.BASE64Encoder().encode(out.toByteArray());

            System.out.println(resContent);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return resContent;
    }

    public static void main(String[] args) throws Exception{
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        Robot robot = new Robot();

        BufferedImage bufferedImage = robot.createScreenCapture(new Rectangle(d.width, d.height));
        String path = "/Users/fanghui/Desktop/screen/" + System.currentTimeMillis() + ".jpg";
        ImageIO.write(bufferedImage, "jpg", new File(path));
        System.out.println(new Gson().toJson(ToolImageUtil.getImageInfo(path)));
        ToolImageUtil.toGrayImage(path);
        ToolImageUtil.toBinaryImage(path);
        ToolImageUtil.scale(path, null, 10, true);
        ToolImageUtil.scale(path, null, 10, false);
        ToolImageUtil.cutImage(path, null, 100, 100, 300, 300);
        ToolImageUtil.convert(path, null, "png");
        String path1 = "/Users/fanghui/Desktop/screen/123456.jpg";
        String path2 = "/Users/fanghui/Desktop/screen/1234567.jpg";

        ToolImageUtil.getImageRGB(path);
        ToolImageUtil.imageDifference(path1, path2, null);
        System.out.println("ok");
        System.out.println(path1.substring(0, 1).equals("/"));

        String paths = "/Users/fanghui/Desktop/screen/123.png";
        ToolImageUtil.toGrayImage(path);
        ToolImageUtil.resize(new File("/Users/fanghui/Desktop/screen/123_gray.png"), new File("/Users/fanghui/Desktop/screen/123_gray_to.png"), 1700, Float.parseFloat("0.03"));
        String oldConten = ToolImageUtil.getImageStr(paths);
        ToolImageUtil.byte64ToZoomByte64Gray(oldConten, (float)0.01);
    }
}
